import { Injectable, Inject, Logger } from '@nestjs/common';
import type {
  ExceptionInterface,
  ExceptionResult,
  ExceptionResponse,
  ExceptionContext,
  ExceptionOptions,
  ExceptionFilterInterface,
  ExceptionReporterInterface,
  ExceptionStatsInterface,
} from './exception.interface';
import { ExceptionType, ExceptionSeverity } from './exception.interface';

/**
 * 异常服务
 * 基于 NestJS 官方示例实现
 * 参考: https://docs.nestjs.cn/fundamentals/exception-filters
 * 对应 Java: 异常处理服务
 */
@Injectable()
export class ExceptionService implements ExceptionInterface {
  private readonly logger = new Logger(ExceptionService.name);

  constructor(
    @Inject('EXCEPTION_FILTER_PROVIDER')
    private readonly exceptionFilter: ExceptionFilterInterface,
    @Inject('EXCEPTION_REPORTER_PROVIDER')
    private readonly exceptionReporter: ExceptionReporterInterface,
    @Inject('EXCEPTION_STATS_PROVIDER')
    private readonly exceptionStats: ExceptionStatsInterface,
  ) {}

  /**
   * 处理异常
   */
  async handle(exception: any, context?: any): Promise<ExceptionResult> {
    try {
      // 记录异常统计
      this.exceptionStats.record(exception, context);

      // 判断是否应该处理
      if (!this.exceptionFilter.shouldHandle(exception)) {
        return {
          success: false,
          response: this.format(exception, context),
          logged: false,
          reported: false,
          handled: false,
        };
      }

      // 处理异常
      const result = await this.exceptionFilter.handle(exception, context);

      // 记录异常
      if (this.shouldLog(exception)) {
        await this.log(exception, context);
        result.logged = true;
      }

      // 上报异常
      if (this.shouldReport(exception)) {
        const reported = await this.exceptionReporter.report(
          exception,
          context,
        );
        result.reported = reported;
      }

      result.handled = true;
      return result;
    } catch (error) {
      this.logger.error('Failed to handle exception', error);
      return {
        success: false,
        response: this.format(exception, context),
        logged: false,
        reported: false,
        handled: false,
      };
    }
  }

  /**
   * 记录异常
   */
  async log(exception: any, context?: any): Promise<void> {
    try {
      const type = this.getType(exception);
      const severity = this.getSeverity(exception);
      const message = this.getMessage(exception);
      const stack = this.getStackTrace(exception);

      const logData = {
        type,
        severity,
        message,
        stack,
        context: this.sanitizeContext(context),
        timestamp: new Date().toISOString(),
      };

      switch (severity) {
        case ExceptionSeverity.CRITICAL:
          this.logger.fatal(`Exception: ${message}`, logData);
          break;
        case ExceptionSeverity.HIGH:
          this.logger.error(`Exception: ${message}`, logData);
          break;
        case ExceptionSeverity.MEDIUM:
          this.logger.warn(`Exception: ${message}`, logData);
          break;
        case ExceptionSeverity.LOW:
          this.logger.log(`Exception: ${message}`, logData);
          break;
        default:
          this.logger.debug(`Exception: ${message}`, logData);
      }
    } catch (error) {
      this.logger.error('Failed to log exception', error);
    }
  }

  /**
   * 格式化异常响应
   */
  format(exception: any, context?: any): ExceptionResponse {
    const type = this.getType(exception);
    const severity = this.getSeverity(exception);
    const message = this.getMessage(exception);
    const statusCode = this.getStatusCode(exception);
    const code = this.getCode(exception);

    const response: ExceptionResponse = {
      statusCode,
      message,
      error: this.getErrorName(exception),
      timestamp: new Date().toISOString(),
      type,
      severity,
      code,
    };

    // 添加请求信息
    if (context?.request) {
      response.path = context.request.url;
      response.method = context.request.method;
    }

    // 添加追踪信息
    if (context?.trace) {
      response.traceId = context.trace.traceId;
      response.spanId = context.trace.spanId;
      response.correlationId = context.trace.correlationId;
    }

    // 添加详细信息
    if (this.shouldIncludeDetails(exception)) {
      response.details = this.getDetails(exception);
    }

    return response;
  }

  /**
   * 判断异常类型
   */
  getType(exception: any): ExceptionType {
    if (exception.name) {
      const name = exception.name.toLowerCase();

      if (name.includes('validation') || name.includes('badrequest')) {
        return ExceptionType.VALIDATION;
      }
      if (name.includes('unauthorized') || name.includes('authentication')) {
        return ExceptionType.AUTHENTICATION;
      }
      if (name.includes('forbidden') || name.includes('authorization')) {
        return ExceptionType.AUTHORIZATION;
      }
      if (name.includes('notfound')) {
        return ExceptionType.NOT_FOUND;
      }
      if (name.includes('conflict')) {
        return ExceptionType.CONFLICT;
      }
      if (name.includes('timeout')) {
        return ExceptionType.TIMEOUT;
      }
      if (name.includes('ratelimit')) {
        return ExceptionType.RATE_LIMIT;
      }
      if (name.includes('database') || name.includes('db')) {
        return ExceptionType.DATABASE;
      }
      if (name.includes('cache')) {
        return ExceptionType.CACHE;
      }
      if (name.includes('network') || name.includes('connection')) {
        return ExceptionType.NETWORK;
      }
      if (name.includes('external') || name.includes('api')) {
        return ExceptionType.EXTERNAL_API;
      }
      if (name.includes('business')) {
        return ExceptionType.BUSINESS;
      }
      if (name.includes('system')) {
        return ExceptionType.SYSTEM;
      }
    }

    if (exception.statusCode) {
      switch (exception.statusCode) {
        case 400:
          return ExceptionType.BAD_REQUEST;
        case 401:
          return ExceptionType.AUTHENTICATION;
        case 403:
          return ExceptionType.AUTHORIZATION;
        case 404:
          return ExceptionType.NOT_FOUND;
        case 409:
          return ExceptionType.CONFLICT;
        case 429:
          return ExceptionType.RATE_LIMIT;
        case 500:
          return ExceptionType.INTERNAL_SERVER_ERROR;
        case 503:
          return ExceptionType.SERVICE_UNAVAILABLE;
        default:
          return ExceptionType.UNKNOWN;
      }
    }

    return ExceptionType.UNKNOWN;
  }

  /**
   * 获取异常严重程度
   */
  getSeverity(exception: any): ExceptionSeverity {
    const type = this.getType(exception);

    switch (type) {
      case ExceptionType.INTERNAL_SERVER_ERROR:
      case ExceptionType.SYSTEM:
        return ExceptionSeverity.CRITICAL;
      case ExceptionType.AUTHENTICATION:
      case ExceptionType.AUTHORIZATION:
      case ExceptionType.DATABASE:
      case ExceptionType.NETWORK:
        return ExceptionSeverity.HIGH;
      case ExceptionType.VALIDATION:
      case ExceptionType.BAD_REQUEST:
      case ExceptionType.CONFLICT:
      case ExceptionType.CACHE:
      case ExceptionType.EXTERNAL_API:
        return ExceptionSeverity.MEDIUM;
      case ExceptionType.NOT_FOUND:
      case ExceptionType.TIMEOUT:
      case ExceptionType.RATE_LIMIT:
      case ExceptionType.BUSINESS:
        return ExceptionSeverity.LOW;
      default:
        return ExceptionSeverity.MEDIUM;
    }
  }

  /**
   * 判断是否应该记录
   */
  shouldLog(exception: any): boolean {
    const severity = this.getSeverity(exception);
    return severity !== ExceptionSeverity.LOW;
  }

  /**
   * 判断是否应该上报
   */
  shouldReport(exception: any): boolean {
    const severity = this.getSeverity(exception);
    return (
      severity === ExceptionSeverity.HIGH ||
      severity === ExceptionSeverity.CRITICAL
    );
  }

  // ==================== 工具方法 ====================

  /**
   * 获取异常消息
   */
  private getMessage(exception: any): string {
    if (exception.message) {
      return exception.message;
    }
    if (typeof exception === 'string') {
      return exception;
    }
    return 'Unknown error occurred';
  }

  /**
   * 获取异常名称
   */
  private getErrorName(exception: any): string {
    if (exception.name) {
      return exception.name;
    }
    if (exception.constructor?.name) {
      return exception.constructor.name;
    }
    return 'Error';
  }

  /**
   * 获取状态码
   */
  private getStatusCode(exception: any): number {
    if (exception.statusCode) {
      return exception.statusCode;
    }
    if (exception.status) {
      return exception.status;
    }

    const type = this.getType(exception);
    switch (type) {
      case ExceptionType.VALIDATION:
      case ExceptionType.BAD_REQUEST:
        return 400;
      case ExceptionType.AUTHENTICATION:
        return 401;
      case ExceptionType.AUTHORIZATION:
        return 403;
      case ExceptionType.NOT_FOUND:
        return 404;
      case ExceptionType.CONFLICT:
        return 409;
      case ExceptionType.RATE_LIMIT:
        return 429;
      case ExceptionType.SERVICE_UNAVAILABLE:
        return 503;
      case ExceptionType.INTERNAL_SERVER_ERROR:
      default:
        return 500;
    }
  }

  /**
   * 获取错误码
   */
  private getCode(exception: any): string | undefined {
    if (exception.code) {
      return exception.code;
    }
    if (exception.errorCode) {
      return exception.errorCode;
    }
    return undefined;
  }

  /**
   * 获取堆栈跟踪
   */
  private getStackTrace(exception: any): string | undefined {
    if (exception.stack) {
      return exception.stack;
    }
    return undefined;
  }

  /**
   * 获取详细信息
   */
  private getDetails(exception: any): any {
    const details: any = {};

    if (exception.details) {
      details.details = exception.details;
    }

    if (exception.cause) {
      details.cause = exception.cause;
    }

    if (exception.context) {
      details.context = exception.context;
    }

    return Object.keys(details).length > 0 ? details : undefined;
  }

  /**
   * 判断是否应该包含详细信息
   */
  private shouldIncludeDetails(exception: any): boolean {
    const severity = this.getSeverity(exception);
    return (
      severity === ExceptionSeverity.HIGH ||
      severity === ExceptionSeverity.CRITICAL
    );
  }

  /**
   * 清理上下文
   */
  private sanitizeContext(context: any): any {
    if (!context) return context;

    const sanitized = { ...context };

    // 清理敏感信息
    if (sanitized.request) {
      sanitized.request = this.sanitizeRequest(sanitized.request);
    }

    if (sanitized.user) {
      sanitized.user = this.sanitizeUser(sanitized.user);
    }

    return sanitized;
  }

  /**
   * 清理请求信息
   */
  private sanitizeRequest(request: any): any {
    if (!request) return request;

    const sanitized = { ...request };

    // 清理敏感头部
    if (sanitized.headers) {
      const sensitiveHeaders = [
        'authorization',
        'cookie',
        'x-api-key',
        'x-auth-token',
      ];
      for (const header of sensitiveHeaders) {
        if (sanitized.headers[header]) {
          sanitized.headers[header] = '[REDACTED]';
        }
      }
    }

    // 清理敏感请求体
    if (sanitized.body) {
      sanitized.body = this.sanitizeBody(sanitized.body);
    }

    return sanitized;
  }

  /**
   * 清理用户信息
   */
  private sanitizeUser(user: any): any {
    if (!user) return user;

    const sanitized = { ...user };

    // 清理敏感用户信息
    const sensitiveFields = ['password', 'token', 'secret', 'key'];
    for (const field of sensitiveFields) {
      if (sanitized[field]) {
        sanitized[field] = '[REDACTED]';
      }
    }

    return sanitized;
  }

  /**
   * 清理请求体
   */
  private sanitizeBody(body: any): any {
    if (!body) return body;

    if (typeof body === 'string') {
      try {
        const parsed = JSON.parse(body);
        return this.sanitizeObject(parsed);
      } catch {
        return body;
      }
    }

    if (typeof body === 'object') {
      return this.sanitizeObject(body);
    }

    return body;
  }

  /**
   * 清理对象
   */
  private sanitizeObject(obj: any): any {
    if (!obj || typeof obj !== 'object') return obj;

    const sanitized = { ...obj };
    const sensitiveFields = ['password', 'token', 'secret', 'key', 'auth'];

    for (const field of sensitiveFields) {
      if (sanitized[field]) {
        sanitized[field] = '[REDACTED]';
      }
    }

    return sanitized;
  }

  // ==================== 装饰器支持 ====================

  /**
   * 异常装饰器实现
   */
  async handleWithOptions<T>(
    options: ExceptionOptions,
    fn: () => T | Promise<T>,
  ): Promise<T> {
    try {
      return await fn();
    } catch (error) {
      const result = await this.handle(error, options);

      if (result.handled) {
        throw error;
      }

      return result.response as any;
    }
  }
}
